#pragma once
#include "oj_model.hpp"
#include "oj_view.hpp"
#include "../common/Util.hpp"
#include "../common/Log.hpp"
#include <vector>
#include <queue>
#include <mutex>
#include <map>
#include <set>
#include <unordered_set>
#include <jsoncpp/json/json.h>
#include "../common/httplib.h"
namespace ns_control{

    using namespace ns_model;
    using namespace ns_Util;
    using namespace ns_view;
    using namespace ns_Log;
    using namespace httplib;
    //负载因子如果是正数，表示该台机器在线
    //为负数则为不在线
    //目前0未定义，也表示该机器不在线
    class Machine{
        std::string _ip;
        uint16_t _port;
        int64_t _load;
        std::mutex *_mtx; //C++11所支持的这个锁是禁止拷贝的，但是这个主机类是支持拷贝，因此使用指针
        public:
        Machine(std::string ip = "",uint16_t port = 0,int64_t load = 0,std::mutex* _mtx = nullptr)
        :_ip(ip)
        ,_port(port)
        ,_load(load)
        ,_mtx(_mtx)
        {

        }
        void IncLoad()
        {
            if(!_mtx){
            std::cerr<<lg(Warning,__FILE__,__LINE__)<<"ip:"<<_ip<<" port："<<_port <<"主机未上线"<<endl;
            return;
            }

            _mtx->lock();
            _load++;
            _mtx->unlock();
        }
        void DecLoad()
        {
            if(!_mtx) {
            std::cerr<<lg(Warning,__FILE__,__LINE__)<<"ip:"<<_ip<<" port："<<_port <<"主机未上线"<<endl;
            return;
            }
            
            _mtx->lock();
            _load--;
            _mtx->unlock();
        }   
        int64_t GetLoad()
        {
            if(!_mtx) {
            std::cerr<<lg(Warning,__FILE__,__LINE__)<<"ip:"<<_ip<<" port："<<_port <<"主机未上线"<<endl;
            return -1;
            }
            int64_t load = 0;
            _mtx->lock();
            load = _load;
            _mtx->unlock();
            return load;
        }
        bool operator>(const Machine& m) const
        {
            if (_load != m._load) return _load > m._load;
            if (_port != m._port) return _port > m._port;
            return _ip > m._ip;
        }

        bool operator>=(const Machine& m) const
        {
            return (*this > m) || (*this == m);
        }

        bool operator<(const Machine& m) const
        {
            return !(*this >= m);
        }

        bool operator<=(const Machine& m) const
        {
            return !(*this > m);
        }

        bool operator==(const Machine& m) const
        {
            return _load == m._load && _port == m._port && _ip == m._ip;
        }

        bool operator!=(const Machine& m) const
        {
            return !(*this == m);
        }
        std::string GetIp() const
        {
            return _ip;
        }
        uint16_t GetPort() const
        {
            return _port;
        }
        //对外提供锁,注意，除了在_loadBalance析构里面调用该函数，请不要在其他地方调用此函数，
        //一般来说，负载均衡器是伴随服务器一直存在的，但是也依然存在可能需要重启均衡器的情况
        //这里提供锁以便释放内存，防止造成对应的内存没有释放的问题
        std::mutex* GetMutex() const
        {
            return _mtx;
        }
        void ClearLoad()
        {
            _mtx->lock();
            _load = 0;
            _mtx->unlock();
        }
        //对外提供主机信息
        std::string GetInfo() const
        {
            return _ip + ":" + std::to_string(_port) + ":" + std::to_string(_load);
        }
        ~Machine()
        {
            
        }
        

    };

    const std::string service_machine_conf = "./conf/compiler_run.conf";
    class LoadBalance{
        //考虑到可能需要上下线某一特定主机，同时需要负载均衡
        //一开始加载主机，所有主机全部加载进入离线主机
        vector<Machine> _AllMachines;
        std::unordered_set<int> _online_machine;
        std::unordered_set<int> _offline_machine;
        std::mutex _mtx;
        public:
        LoadBalance()
        {
            assert(LoadConf()); std::cout<<lg(Info,__FILE__,__LINE__)<<"加载"<<service_machine_conf<<"成功\n";
        }

        //加载配置主机文件
        bool LoadConf()
        {
            std::ifstream ifs(service_machine_conf);
            if(!ifs.is_open())
            {
                //加载配置文件失败
                std::cerr<<lg(Fatal,__FILE__,__LINE__ - 2)<<"：conf/compiler_run.conf编译服务主机配置文件加载失败，请检查配置文件，或者重启服务";
                return false;
            }
            std::string line;
            int linecnt = 0;
            while(!ifs.eof())
            {
                std::getline(ifs,line);
                linecnt++;
                line = ns_Util::StringUtil::RemoveAfterHash(line);
                if(line.empty())
                {
                    //去除纯注释行
                    continue;
                }
                std::vector<std::string> SepRes =  ns_Util::StringUtil::SepString(line," \t\n");

                if(SepRes.size() ==2 )
                {
                    Machine m(SepRes[0],std::stod(SepRes[1]),0,new std::mutex);
                    _AllMachines.push_back(m);
                }
                else{
                    std::cerr<<lg(Warning,__FILE__,__LINE__)<< ":配置文件"<<linecnt<<":出现格式问题，可能读取出错，故而本行不读取本行主机数据\n";
                }
            }
            ifs.close();
            return true;
        }

        //返回该主机情况，以及该主机的id
        bool SmartChoice(int* id,Machine *&outm)
        {
            //1.选择负载最小的主机
            //2.离线上线该主机
            {
                std::unique_lock l_lock (_mtx);
                int online_num = _online_machine.size();
                if(online_num == 0)
                {
                    std::cerr<<lg(Fatal,__FILE__,__LINE__)<< "后端服务器全部断开，请见检查服务器情况。\n";
                    return false;
                }
                auto it = _online_machine.begin();
                auto ans = it++;
                while(it != _online_machine.end())
                {
                    if(_AllMachines[*it].GetLoad() < _AllMachines[*ans].GetLoad())
                    {
                        ans = it;
                    }
                    it++;
                }
                outm =&_AllMachines[*ans] ;
                *id = *ans - 0;
            }
            return 1;
        } 
        //可能会支持根据主机IP和端口号上线下线
        bool offlineMachine(int MachineId)
        {
            std::lock_guard lk(_mtx);
            _AllMachines[MachineId].ClearLoad();
            _online_machine.erase(MachineId);
            _offline_machine.insert(MachineId);
            return 1;
        }
        //需要做负载因子初始化，暂时没有做检测主机行为
        bool OnlineMachine()
        {
            std::lock_guard lk(_mtx);
            for(int i = 0;i<_AllMachines.size();i++)
            {
                _online_machine.insert(i);
                _offline_machine.erase(i);
            }
            return 1;
        }
        void ShowOnlineMachine(){
            std::unique_lock l_lock (_mtx);
            std::cout<<"当前在线主机信息如下："<<std::endl;
            for(auto& m:_online_machine)
            {
                std::cout<<m <<"\t"<<_AllMachines[m].GetIp()
                <<"\t"<<_AllMachines[m].GetPort()
                <<"\t"<<_AllMachines[m].GetLoad()
                <<std::endl;
            }
            std::cout<<std::endl;
        }
        ~LoadBalance()
        {
            //释放_AllMachine
            for(auto& m:_AllMachines)
            {
                delete m.GetMutex();
            }
        }
 };

    class Control{
        
        Model _model;//后台题目数据
        view _view;//提供渲染成html
        LoadBalance _loadBalance;
        public:
        Control()
        {
            //暂时先上线全部主机，后序调整
            _loadBalance.OnlineMachine();
        }
        bool AllQuestionsList(std::string* html)
        {
            std::vector<Question> AllQuestions;
            if(_model.GetAllQuestions(&AllQuestions))
            {
                if(!_view.AllExpand(AllQuestions,html))                {
                    std::cerr<<"题目渲染失败"<<lg(Fatal,__FILE__,__LINE__);
                    return false;
                }
            }
            else
            {
                std::cerr<<"题目获取失败"<<lg(Fatal,__FILE__,__LINE__);
                return false;
            }
            return true;
        }
        bool OneQuestionContent(const std::string& number, std::string * html)
        {
            Question q;
            if(_model.GetOneQuestion(number,&q))
            {
                _view.OneExpand(q,html);
            }
            else
            {
                *html = "获取题号"+ number +"失败";
                std::cerr<<"题目获取失败"<<lg(Warning,__FILE__,__LINE__);
                return false;
            }
            return 1;
        }

        void RecoveryHost()
        {
            _loadBalance.OnlineMachine();
            std::cout<<lg(Info,__FILE__,__LINE__)<<"主机恢复成功"<<std::endl;
        }

        //暂时还没有做主机掉线后重新重连的处理
        void GetRunResult(const std::string& number,const std::string& in_json,std::string* out_json)
        {

            std::cout<<lg(Debug,__FILE__,__LINE__)<<"收到的json串"<<in_json<<std::endl;
            //1. in_json 反序列化，取得用户所得的代码
            //2. 拼接用户代码，形成.cpp的文件内容
            //3.选择负载最低的主机
            //4.发起http请求
            //5.获取http请求的结果赋值拿给json

            //1.反序列化+形成输入编译所需json串
            Question q;
            _model.GetOneQuestion(number,&q);
            Json::Reader reader;
            Json::Value in_value;
            reader.parse(in_json,in_value);
            std::string code = in_value["code"].asString();

            Json::Value CpValue;
            CpValue["input"] = in_value["input"].asString();
            CpValue["code"] = code + q._main_cpp;
            CpValue["CPU_limit"] = q._CPU_limit;
            CpValue["Memory_limit"] = q._Memory_limit;

            Json::FastWriter writer;
            std::string compile_string = writer.write(CpValue);

            //选择主机
            Machine* m;
            int MachineId;
            while(1)
            {
                if(!_loadBalance.SmartChoice(&MachineId,m))
                {   
                    //说明一台主机都没有了,打印Fatal信息
                    std::cerr<<lg(Fatal,__FILE__,__LINE__)<<"没有主机在线"<<endl;

                    break;
                }
                m->IncLoad();
                std::cout<<lg(Info,__FILE__,__LINE__)<<"主机选择成功"<<"主机ID:"<< MachineId<<"主机信息"<<m->GetInfo()<<endl;
                Client cli(m->GetIp(),m->GetPort());
                // cli.set_connection_timeout(5000 * 100); // 
                // // 设置读取超时（单位为毫秒）
                // cli.set_read_timeout(5000 * 1000); // 
                auto res = cli.Post("/compile_and_run",CpValue.toStyledString(),"application/json;charset=utf-8");
                if(res)
                {
                    if(res->status ==200)
                    {
                        *out_json = res->body;
                        m->DecLoad();
                        std::cout<<lg(MsgLevel::Info,__FILE__,__LINE__)<<"主机ID:"<< MachineId<<"主机信息"<<m->GetInfo()<<endl<<"请求编译运行成功\n";
                        break;
                    }
                    else
                    {
                        std::cerr<<lg(MsgLevel::Error,__FILE__,__LINE__)<<"主机请求错误:"<<to_string(res.error())<<endl<<"主机ID:"<< MachineId<<"  主机信息"<<m->GetInfo()<<std::endl;
                        m->DecLoad();
                    }

                }
                else
                {
                    std::cerr<<lg(MsgLevel::Error,__FILE__,__LINE__)<<"请求主机失败，主机信息"<<m->GetInfo()<<std::endl<<"主机ID:"<< MachineId<<"主机已经离线"<<std::endl;
                    _loadBalance.offlineMachine(MachineId);
                    _loadBalance.ShowOnlineMachine();
                }
                
            }
            
        }
    };

};